---
title: 型別系統與介面
---

本模組探討 Go 的型別系統和介面，這是 Go 設計哲學的基礎。Go 的型別系統是靜態型別的，但透過介面提供了一些動態特性，在安全性和靈活性之間取得了平衡，這與 JavaScript 的動態型別有顯著不同。

## Go 型別系統概述

Go 是一種靜態型別語言，意味著型別在編譯時檢查。與 JavaScript 的動態型別相比，這提供了更好的效能、更早的錯誤檢測和更清晰的程式碼文件。

### 靜態型別 vs 動態型別

<UniversalEditor title="型別系統對比" compare={true}>
```javascript !! js
// JavaScript: 動態型別
let value = 42;           // number
console.log(typeof value); // "number"

value = "hello";          // string
console.log(typeof value); // "string"

value = { name: "John" }; // object
console.log(typeof value); // "object"

// 型別在執行時檢查
function processData(data) {
    return data.length; // 對字串、陣列有效，但對數字失敗
}

processData("hello"); // 5
processData([1, 2, 3]); // 3
processData(42); // TypeError: data.length is not a function
```

```go !! go
// Go: 靜態型別
package main

import "fmt"

func main() {
    var value int = 42
    fmt.Printf("Type: %T, Value: %v\n", value, value) // Type: int, Value: 42
    
    // value = "hello" // 編譯錯誤: cannot use "hello" (type string) as int
    
    // 型別在編譯時檢查
    processData("hello") // 有效
    processData([]int{1, 2, 3}) // 有效
    // processData(42) // 編譯錯誤: cannot use 42 (type int) as string
}

func processData(data string) {
    fmt.Println(len(data))
}
```
</UniversalEditor>

## 基本型別

Go 提供了比 JavaScript 原始型別更豐富的顯式基本型別集合。

### 數值型別

<UniversalEditor title="數值型別對比" compare={true}>
```javascript !! js
// JavaScript: 單一數字型別 (64位浮點數)
let integer = 42;
let float = 3.14;
let bigInt = 9007199254740991n; // BigInt 用於大整數

console.log(typeof integer); // "number"
console.log(typeof float);   // "number"
console.log(typeof bigInt);  // "bigint"

// 所有數字都是 64 位浮點數 (除了 BigInt)
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
```

```go !! go
// Go: 多種數值型別用於不同用例
package main

import "fmt"

func main() {
    // 整數型別
    var int8Val int8 = 127        // 8位有符號整數 (-128 到 127)
    var int16Val int16 = 32767    // 16位有符號整數
    var int32Val int32 = 2147483647 // 32位有符號整數
    var int64Val int64 = 9223372036854775807 // 64位有符號整數
    var intVal int = 42           // 平台相關 (32 或 64 位)
    
    // 無符號整數型別
    var uint8Val uint8 = 255      // 8位無符號整數 (0 到 255)
    var uint16Val uint16 = 65535  // 16位無符號整數
    var uint32Val uint32 = 4294967295 // 32位無符號整數
    var uint64Val uint64 = 18446744073709551615 // 64位無符號整數
    var uintVal uint = 42         // 平台相關
    
    // 浮點型別
    var float32Val float32 = 3.14 // 32位浮點數
    var float64Val float64 = 3.14159265359 // 64位浮點數
    
    // 複數型別
    var complex64Val complex64 = 3 + 4i  // 64位複數
    var complex128Val complex128 = 3 + 4i // 128位複數
    
    fmt.Printf("int8: %T, uint8: %T, float32: %T\n", int8Val, uint8Val, float32Val)
}
```
</UniversalEditor>

### 字串和布林型別

<UniversalEditor title="字串和布林型別對比" compare={true}>
```javascript !! js
// JavaScript: 字串和布林值
let str = "Hello, World!";
let bool = true;

console.log(typeof str);  // "string"
console.log(typeof bool); // "boolean"

// 字串操作
console.log(str.length);           // 13
console.log(str.toUpperCase());    // "HELLO, WORLD!"
console.log(str.includes("World")); // true

// 布林操作
console.log(!bool);                // false
console.log(bool && true);         // true
console.log(bool || false);        // true
```

```go !! go
// Go: 字串和布林值
package main

import (
    "fmt"
    "strings"
)

func main() {
    var str string = "Hello, World!"
    var boolVal bool = true
    
    fmt.Printf("str type: %T, bool type: %T\n", str, boolVal)
    
    // 字串操作
    fmt.Println(len(str))                    // 13
    fmt.Println(strings.ToUpper(str))        // "HELLO, WORLD!"
    fmt.Println(strings.Contains(str, "World")) // true
    
    // 布林操作
    fmt.Println(!boolVal)                    // false
    fmt.Println(boolVal && true)             // true
    fmt.Println(boolVal || false)            // true
    
    // Go 中的字串是不可變的
    // str[0] = 'h' // 編譯錯誤: cannot assign to str[0]
}
```
</UniversalEditor>

## 複合型別

Go 提供了幾種複合型別，允許將值組合在一起。

### 陣列和切片

<UniversalEditor title="陣列和切片對比" compare={true}>
```javascript !! js
// JavaScript: 陣列 (動態)
let arr = [1, 2, 3, 4, 5];
console.log(arr.length); // 5

// 動態調整大小
arr.push(6);             // [1, 2, 3, 4, 5, 6]
arr.pop();               // [1, 2, 3, 4, 5]
arr.splice(1, 2);        // [1, 4, 5]

// 陣列方法
let doubled = arr.map(x => x * 2);     // [2, 8, 10]
let sum = arr.reduce((a, b) => a + b); // 10

// 切片
let slice = arr.slice(1, 3);           // [4, 5]
```

```go !! go
// Go: 陣列 (固定大小) 和切片 (動態)
package main

import "fmt"

func main() {
    // 陣列: 固定大小
    var arr [5]int = [5]int{1, 2, 3, 4, 5}
    fmt.Println(len(arr)) // 5
    
    // 無法調整陣列大小
    // arr = append(arr, 6) // 編譯錯誤: cannot use append with array
    
    // 切片: 動態大小
    var slice []int = []int{1, 2, 3, 4, 5}
    fmt.Println(len(slice)) // 5
    
    // 動態調整大小
    slice = append(slice, 6) // [1, 2, 3, 4, 5, 6]
    slice = slice[:len(slice)-1] // 移除最後一個元素: [1, 2, 3, 4, 5]
    
    // 切片操作
    subSlice := slice[1:3] // [2, 3]
    
    // 陣列到切片的轉換
    sliceFromArray := arr[:] // 將陣列轉換為切片
    
    fmt.Printf("Array: %v, Slice: %v\n", arr, slice)
}
```
</UniversalEditor>

### 映射

<UniversalEditor title="映射對比" compare={true}>
```javascript !! js
// JavaScript: 物件和映射
let obj = { name: "John", age: 30 };
let map = new Map();
map.set("name", "John");
map.set("age", 30);

console.log(obj.name);           // "John"
console.log(map.get("name"));    // "John"

// 物件操作
obj.city = "New York";           // 新增屬性
delete obj.age;                  // 刪除屬性
console.log("name" in obj);      // true

// 映射操作
map.set("city", "New York");     // 新增鍵值對
map.delete("age");               // 刪除鍵
console.log(map.has("name"));    // true
```

```go !! go
// Go: 映射
package main

import "fmt"

func main() {
    // 映射宣告和初始化
    var m map[string]int = make(map[string]int)
    m["name"] = 1
    m["age"] = 30
    
    // 或使用字面量語法
    person := map[string]interface{}{
        "name": "John",
        "age":  30,
        "city": "New York",
    }
    
    // 存取值
    fmt.Println(person["name"]) // "John"
    
    // 檢查鍵是否存在
    if age, exists := person["age"]; exists {
        fmt.Printf("Age: %v\n", age)
    }
    
    // 新增/更新值
    person["country"] = "USA"
    
    // 刪除值
    delete(person, "age")
    
    // 遍歷映射
    for key, value := range person {
        fmt.Printf("%s: %v\n", key, value)
    }
}
```
</UniversalEditor>

### 結構體

<UniversalEditor title="結構體對比" compare={true}>
```javascript !! js
// JavaScript: 物件和類
let person = {
    name: "John",
    age: 30,
    city: "New York"
};

// ES6 類
class Person {
    constructor(name, age, city) {
        this.name = name;
        this.age = age;
        this.city = city;
    }
    
    greet() {
        return `Hello, I'm ${this.name}`;
    }
}

let john = new Person("John", 30, "New York");
console.log(john.greet()); // "Hello, I'm John"

// 物件解構
let { name, age } = person;
console.log(name, age); // "John" 30
```

```go !! go
// Go: 結構體
package main

import "fmt"

// 結構體定義
type Person struct {
    Name string
    Age  int
    City string
}

// 結構體上的方法
func (p Person) Greet() string {
    return fmt.Sprintf("Hello, I'm %s", p.Name)
}

// 帶指標接收者的方法 (可以修改結構體)
func (p *Person) SetAge(age int) {
    p.Age = age
}

func main() {
    // 建立結構體實例
    person := Person{
        Name: "John",
        Age:  30,
        City: "New York",
    }
    
    // 存取欄位
    fmt.Println(person.Name) // "John"
    
    // 呼叫方法
    fmt.Println(person.Greet()) // "Hello, I'm John"
    
    // 修改結構體
    person.SetAge(31)
    fmt.Println(person.Age) // 31
    
    // 結構體嵌入 (組合)
    type Employee struct {
        Person
        Salary float64
    }
    
    emp := Employee{
        Person: Person{Name: "Jane", Age: 25, City: "Boston"},
        Salary: 50000,
    }
    
    // 存取嵌入結構體的方法
    fmt.Println(emp.Greet()) // "Hello, I'm Jane"
}
```
</UniversalEditor>

## 介面

介面是 Go 最強大的特性之一，提供了一種定義行為而不涉及實作細節的方式。

### 介面基礎

<UniversalEditor title="介面基礎對比" compare={true}>
```javascript !! js
// JavaScript: 鴨子型別 (隱式介面)
function processData(data) {
    if (typeof data.length === 'number' && typeof data[0] !== 'undefined') {
        return data.length;
    }
    return 0;
}

// 任何具有 'length' 屬性的物件都可以工作
console.log(processData([1, 2, 3]));     // 3
console.log(processData("hello"));       // 5
console.log(processData({ length: 10 })); // 10

// ES6 類和方法
class Shape {
    area() {
        return 0;
    }
}

class Circle extends Shape {
    constructor(radius) {
        super();
        this.radius = radius;
    }
    
    area() {
        return Math.PI * this.radius ** 2;
    }
}

let circle = new Circle(5);
console.log(circle.area()); // 78.54...
```

```go !! go
// Go: 顯式介面
package main

import (
    "fmt"
    "math"
)

// 介面定義
type Measurable interface {
    Area() float64
}

// 實作介面的結構體
type Circle struct {
    Radius float64
}

// 方法實作
func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

type Rectangle struct {
    Width  float64
    Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// 與任何實作 Measurable 的型別一起工作的函數
func processShape(m Measurable) {
    fmt.Printf("Area: %.2f\n", m.Area())
}

func main() {
    circle := Circle{Radius: 5}
    rectangle := Rectangle{Width: 4, Height: 6}
    
    // 兩種型別都實作了 Measurable 介面
    processShape(circle)    // Area: 78.54
    processShape(rectangle) // Area: 24.00
    
    // 介面滿足是隱式的
    var m Measurable = circle
    fmt.Printf("Type: %T, Area: %.2f\n", m, m.Area())
}
```
</UniversalEditor>

### 介面組合

<UniversalEditor title="介面組合對比" compare={true}>
```javascript !! js
// JavaScript: 物件組合和混入
const Movable = {
    move() {
        return "Moving...";
    }
};

const Drawable = {
    draw() {
        return "Drawing...";
    }
};

// 混入模式
class GameObject {
    constructor() {
        Object.assign(this, Movable, Drawable);
    }
}

let gameObj = new GameObject();
console.log(gameObj.move());  // "Moving..."
console.log(gameObj.draw());  // "Drawing..."

// 或使用組合
class Sprite {
    constructor() {
        this.movable = Movable;
        this.drawable = Drawable;
    }
    
    move() {
        return this.movable.move();
    }
    
    draw() {
        return this.drawable.draw();
    }
}
```

```go !! go
// Go: 介面組合
package main

import "fmt"

// 基礎介面
type Movable interface {
    Move() string
}

type Drawable interface {
    Draw() string
}

// 組合介面
type GameObject interface {
    Movable
    Drawable
}

// 實作
type Sprite struct {
    X, Y int
}

func (s *Sprite) Move() string {
    s.X += 1
    s.Y += 1
    return fmt.Sprintf("Moving to (%d, %d)", s.X, s.Y)
}

func (s Sprite) Draw() string {
    return fmt.Sprintf("Drawing at (%d, %d)", s.X, s.Y)
}

// 與任何 GameObject 一起工作的函數
func updateGameObject(obj GameObject) {
    fmt.Println(obj.Move())
    fmt.Println(obj.Draw())
}

func main() {
    sprite := &Sprite{X: 0, Y: 0}
    updateGameObject(sprite)
    
    // 介面組合允許靈活的設計
    var movable Movable = sprite
    var drawable Drawable = sprite
    var gameObj GameObject = sprite
    
    fmt.Println(movable.Move())  // 只能呼叫 Move()
    fmt.Println(drawable.Draw()) // 只能呼叫 Draw()
    fmt.Println(gameObj.Move())  // 可以呼叫 Move() 和 Draw()
    fmt.Println(gameObj.Draw())
}
```
</UniversalEditor>

### 空介面

<UniversalEditor title="空介面對比" compare={true}>
```javascript !! js
// JavaScript: 動態型別允許任何值
function processAnyValue(value) {
    if (typeof value === 'string') {
        return value.toUpperCase();
    } else if (typeof value === 'number') {
        return value * 2;
    } else if (Array.isArray(value)) {
        return value.length;
    } else if (typeof value === 'object') {
        return Object.keys(value).length;
    }
    return "Unknown type";
}

console.log(processAnyValue("hello"));     // "HELLO"
console.log(processAnyValue(42));          // 84
console.log(processAnyValue([1, 2, 3]));   // 3
console.log(processAnyValue({a: 1, b: 2})); // 2
```

```go !! go
// Go: 空介面 (interface{}) 接受任何型別
package main

import (
    "fmt"
    "reflect"
)

// 空介面可以儲存任何值
func processAnyValue(value interface{}) {
    switch v := value.(type) {
    case string:
        fmt.Printf("String: %s\n", v)
    case int:
        fmt.Printf("Integer: %d\n", v)
    case []int:
        fmt.Printf("Slice: %v (length: %d)\n", v, len(v))
    case map[string]int:
        fmt.Printf("Map: %v (keys: %d)\n", v, len(v))
    default:
        fmt.Printf("Unknown type: %T\n", v)
    }
}

func main() {
    processAnyValue("hello")           // String: hello
    processAnyValue(42)                // Integer: 42
    processAnyValue([]int{1, 2, 3})    // Slice: [1 2 3] (length: 3)
    processAnyValue(map[string]int{"a": 1, "b": 2}) // Map: map[a:1 b:2] (keys: 2)
    
    // 使用 reflect 包進行更動態的行為
    var value interface{} = "hello"
    fmt.Printf("Type: %v, Value: %v\n", reflect.TypeOf(value), reflect.ValueOf(value))
}
```
</UniversalEditor>

## 型別斷言和型別開關

Go 提供了與介面型別動態工作的機制。

### 型別斷言

<UniversalEditor title="型別斷言對比" compare={true}>
```javascript !! js
// JavaScript: 型別檢查和轉換
function processValue(value) {
    if (typeof value === 'string') {
        return value.toUpperCase();
    } else if (Array.isArray(value)) {
        return value.join(', ');
    } else if (typeof value === 'object' && value !== null) {
        return JSON.stringify(value);
    }
    return String(value);
}

console.log(processValue("hello"));     // "HELLO"
console.log(processValue([1, 2, 3]));   // "1, 2, 3"
console.log(processValue({a: 1}));      // '{"a":1}'
console.log(processValue(42));          // "42"
```

```go !! go
// Go: 型別斷言
package main

import (
    "encoding/json"
    "fmt"
    "strconv"
)

func processValue(value interface{}) string {
    // 帶逗號 ok 慣用法的型別斷言
    if str, ok := value.(string); ok {
        return str
    }
    
    if arr, ok := value.([]int); ok {
        return fmt.Sprintf("%v", arr)
    }
    
    if m, ok := value.(map[string]int); ok {
        if jsonBytes, err := json.Marshal(m); err == nil {
            return string(jsonBytes)
        }
    }
    
    if num, ok := value.(int); ok {
        return strconv.Itoa(num)
    }
    
    return "unknown type"
}

func main() {
    fmt.Println(processValue("hello"))                    // "hello"
    fmt.Println(processValue([]int{1, 2, 3}))            // "[1 2 3]"
    fmt.Println(processValue(map[string]int{"a": 1}))    // '{"a":1}'
    fmt.Println(processValue(42))                        // "42"
    
    // 不帶逗號 ok 的型別斷言 (如果型別錯誤會 panic)
    var value interface{} = "hello"
    str := value.(string) // 安全斷言
    fmt.Println(str)      // "hello"
    
    // 這會 panic:
    // num := value.(int) // panic: interface conversion
}
```
</UniversalEditor>

### 型別開關

<UniversalEditor title="型別開關對比" compare={true}>
```javascript !! js
// JavaScript: 帶 typeof 的 switch
function describeType(value) {
    switch (typeof value) {
        case 'string':
            return `String: "${value}"`;
        case 'number':
            return `Number: ${value}`;
        case 'boolean':
            return `Boolean: ${value}`;
        case 'object':
            if (Array.isArray(value)) {
                return `Array with ${value.length} elements`;
            } else if (value === null) {
                return 'Null';
            } else {
                return 'Object';
            }
        default:
            return 'Unknown type';
    }
}

console.log(describeType("hello"));     // "String: \"hello\""
console.log(describeType(42));          // "Number: 42"
console.log(describeType(true));        // "Boolean: true"
console.log(describeType([1, 2, 3]));   // "Array with 3 elements"
console.log(describeType({a: 1}));      // "Object"
```

```go !! go
// Go: 型別開關
package main

import "fmt"

func describeType(value interface{}) string {
    switch v := value.(type) {
    case string:
        return fmt.Sprintf("String: %q", v)
    case int:
        return fmt.Sprintf("Integer: %d", v)
    case bool:
        return fmt.Sprintf("Boolean: %t", v)
    case []int:
        return fmt.Sprintf("Slice with %d elements", len(v))
    case map[string]int:
        return fmt.Sprintf("Map with %d keys", len(v))
    case nil:
        return "Nil"
    default:
        return fmt.Sprintf("Unknown type: %T", v)
    }
}

func main() {
    fmt.Println(describeType("hello"))                    // String: "hello"
    fmt.Println(describeType(42))                        // Integer: 42
    fmt.Println(describeType(true))                      // Boolean: true
    fmt.Println(describeType([]int{1, 2, 3}))            // Slice with 3 elements
    fmt.Println(describeType(map[string]int{"a": 1}))    // Map with 1 keys
    fmt.Println(describeType(nil))                       // Nil
    
    // 型別開關也可以用於控制流
    var values []interface{} = []interface{}{"hello", 42, true, []int{1, 2}}
    
    for _, value := range values {
        switch v := value.(type) {
        case string:
            fmt.Printf("Processing string: %s\n", v)
        case int:
            fmt.Printf("Processing integer: %d\n", v)
        case bool:
            fmt.Printf("Processing boolean: %t\n", v)
        case []int:
            fmt.Printf("Processing slice: %v\n", v)
        }
    }
}
```
</UniversalEditor>

## 指標與記憶體引用

指標是 Go 語言的重要特性，它允許程式直接存取和操作記憶體位址。與 JavaScript 的自動記憶體管理不同，Go 提供了更精確的記憶體控制能力，這對系統程式設計和效能最佳化至關重要。

### JavaScript vs Go 記憶體模型對比

<UniversalEditor title="記憶體模型對比" compare={true}>
```javascript !! js
// JavaScript: 自動記憶體管理
let obj1 = { name: "John", age: 30 };
let obj2 = obj1; // 參照賦值

console.log(obj1.name); // "John"
console.log(obj2.name); // "John"

obj2.name = "Jane";
console.log(obj1.name); // "Jane" - 兩個變數指向同一個物件
console.log(obj2.name); // "Jane"

// 基本型別是值傳遞
let a = 5;
let b = a; // 值拷貝
b = 10;
console.log(a); // 5 - a 沒有改變
console.log(b); // 10

// 函數參數傳遞
function modifyObject(obj) {
    obj.name = "Modified"; // 修改原物件
    obj = { name: "New" }; // 重新賦值不影響原物件
}

function modifyPrimitive(val) {
    val = 100; // 不影響原值
}

let person = { name: "Alice" };
let number = 42;

modifyObject(person);
modifyPrimitive(number);

console.log(person.name); // "Modified" - 物件被修改
console.log(number);      // 42 - 原值未改變
```

```go !! go
// Go: 顯式指標操作
package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func main() {
    // 值型別
    person1 := Person{Name: "John", Age: 30}
    person2 := person1 // 值拷貝
    
    fmt.Println("person1:", person1.Name) // "John"
    fmt.Println("person2:", person2.Name) // "John"
    
    person2.Name = "Jane"
    fmt.Println("person1:", person1.Name) // "John" - person1 未改變
    fmt.Println("person2:", person2.Name) // "Jane"
    
    // 指標型別
    ptr1 := &person1 // 取得 person1 的位址
    ptr2 := ptr1     // 指標拷貝，指向同一記憶體位址
    
    fmt.Println("透過 ptr1:", ptr1.Name) // "John"
    fmt.Println("透過 ptr2:", ptr2.Name) // "John"
    
    ptr2.Name = "Alice" // 透過指標修改
    fmt.Println("person1:", person1.Name) // "Alice" - 原變數被修改
    fmt.Println("透過 ptr1:", ptr1.Name) // "Alice"
    
    // 函數參數傳遞演示
    modifyByValue(person1)     // 值傳遞
    modifyByPointer(&person1)  // 指標傳遞
    
    fmt.Println("最終 person1:", person1.Name) // "Modified by pointer"
}

// 值傳遞 - 不影響原變數
func modifyByValue(p Person) {
    p.Name = "Modified by value"
    fmt.Println("函數內值傳遞:", p.Name) // "Modified by value"
}

// 指標傳遞 - 影響原變數
func modifyByPointer(p *Person) {
    p.Name = "Modified by pointer"
    fmt.Println("函數內指標傳遞:", p.Name) // "Modified by pointer"
}
```
</UniversalEditor>

### 指標基礎概念和語法

<UniversalEditor title="指標語法基礎" compare={true}>
```javascript !! js
// JavaScript: 沒有直接的指標概念
// 但可以透過參照來理解指標的概念

let value = 42;
let reference = { value: 42 }; // 用物件包裝來模擬指標

// 無法直接取得變數的記憶體位址
// console.log(&value); // 語法錯誤

// 參照操作
function increment(ref) {
    ref.value++; // 修改參照指向的值
}

increment(reference);
console.log(reference.value); // 43

// 陣列參照範例
let arr1 = [1, 2, 3];
let arr2 = arr1; // 參照賦值

arr2.push(4);
console.log(arr1); // [1, 2, 3, 4] - 原陣列被修改
console.log(arr2); // [1, 2, 3, 4]

// 解除參照
arr2 = null;
console.log(arr1); // [1, 2, 3, 4] - arr1 仍然有效
console.log(arr2); // null
```

```go !! go
// Go: 顯式指標語法
package main

import "fmt"

func main() {
    // 基本指標操作
    var value int = 42
    var ptr *int = &value // 取得 value 的位址
    
    fmt.Printf("value: %d\n", value)         // 42
    fmt.Printf("value 的位址: %p\n", &value)   // 0x...
    fmt.Printf("ptr 儲存的位址: %p\n", ptr)     // 0x... (與上面相同)
    fmt.Printf("ptr 指向的值: %d\n", *ptr)     // 42 (解參照)
    
    // 透過指標修改值
    *ptr = 100 // 解參照並賦值
    fmt.Printf("修改後的 value: %d\n", value)  // 100
    
    // 指標的零值
    var nilPtr *int
    fmt.Printf("nilPtr: %v\n", nilPtr) // <nil>
    
    // 檢查指標是否為 nil
    if nilPtr == nil {
        fmt.Println("nilPtr 是 nil")
    }
    
    // 多級指標
    var ptrToPtr **int = &ptr
    fmt.Printf("ptrToPtr 指向的位址: %p\n", *ptrToPtr)  // ptr 的位址
    fmt.Printf("雙重解參照的值: %d\n", **ptrToPtr)      // 100
    
    // 不同型別的指標
    var str string = "Hello"
    var strPtr *string = &str
    
    fmt.Printf("字串: %s\n", str)
    fmt.Printf("透過指標: %s\n", *strPtr)
    
    *strPtr = "World"
    fmt.Printf("修改後: %s\n", str) // "World"
}
```
</UniversalEditor>

### 指標在函數中的應用

<UniversalEditor title="指標函數參數對比" compare={true}>
```javascript !! js
// JavaScript: 參照傳遞和值傳遞
function swapByValue(a, b) {
    // 對於基本型別，這不會影響原變數
    let temp = a;
    a = b;
    b = temp;
    console.log(`函數內: a=${a}, b=${b}`);
}

function swapByReference(obj) {
    // 可以修改物件的屬性
    let temp = obj.a;
    obj.a = obj.b;
    obj.b = temp;
}

// 基本型別測試
let x = 10, y = 20;
swapByValue(x, y);
console.log(`函數外: x=${x}, y=${y}`); // x=10, y=20 (未改變)

// 物件參照測試
let values = { a: 10, b: 20 };
swapByReference(values);
console.log(`交換後: a=${values.a}, b=${values.b}`); // a=20, b=10

// 陣列作為函數參數
function modifyArray(arr) {
    arr.push(4); // 修改原陣列
    arr = [9, 8, 7]; // 重新賦值不影響原陣列
}

let myArray = [1, 2, 3];
modifyArray(myArray);
console.log(myArray); // [1, 2, 3, 4] - 只有 push 操作生效
```

```go !! go
// Go: 值傳遞 vs 指標傳遞
package main

import "fmt"

// 值傳遞 - 不影響原變數
func swapByValue(a, b int) {
    fmt.Printf("函數內交換前: a=%d, b=%d\n", a, b)
    a, b = b, a
    fmt.Printf("函數內交換後: a=%d, b=%d\n", a, b)
}

// 指標傳遞 - 影響原變數
func swapByPointer(a, b *int) {
    fmt.Printf("函數內交換前: a=%d, b=%d\n", *a, *b)
    *a, *b = *b, *a
    fmt.Printf("函數內交換後: a=%d, b=%d\n", *a, *b)
}

// 結構體值傳遞
func modifyPersonByValue(p Person) {
    p.Name = "Modified"
    p.Age = 100
}

// 結構體指標傳遞
func modifyPersonByPointer(p *Person) {
    p.Name = "Modified by pointer" // 自動解參照
    p.Age = 100
    // 等價於: (*p).Name = "Modified by pointer"
}

type Person struct {
    Name string
    Age  int
}

func main() {
    // 基本型別測試
    x, y := 10, 20
    fmt.Printf("交換前: x=%d, y=%d\n", x, y)
    
    swapByValue(x, y)
    fmt.Printf("值傳遞後: x=%d, y=%d\n", x, y) // 未改變
    
    swapByPointer(&x, &y)
    fmt.Printf("指標傳遞後: x=%d, y=%d\n", x, y) // 已交換
    
    // 結構體測試
    person := Person{Name: "Alice", Age: 25}
    fmt.Printf("原始: %+v\n", person)
    
    modifyPersonByValue(person)
    fmt.Printf("值傳遞後: %+v\n", person) // 未改變
    
    modifyPersonByPointer(&person)
    fmt.Printf("指標傳遞後: %+v\n", person) // 已修改
    
    // 切片和對應的特殊情況
    slice := []int{1, 2, 3}
    modifySlice(slice)
    fmt.Printf("切片修改後: %v\n", slice) // [1, 2, 3, 4] - 切片header是值傳遞，但底層陣列是共享的
}

func modifySlice(s []int) {
    s[0] = 999 // 修改切片元素
    s = append(s, 4) // 在某些情況下可能影響原切片
}
```
</UniversalEditor>

### 指標在結構體方法中的應用

<UniversalEditor title="結構體方法接收者對比" compare={true}>
```javascript !! js
// JavaScript: 類別方法和 this 綁定
class Rectangle {
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }
    
    // 方法自動綁定到實例
    area() {
        return this.width * this.height;
    }
    
    // 修改實例狀態
    scale(factor) {
        this.width *= factor;
        this.height *= factor;
        return this; // 回傳 this 支援鏈式呼叫
    }
    
    // 取得副本
    copy() {
        return new Rectangle(this.width, this.height);
    }
    
    // 比較方法
    equals(other) {
        return this.width === other.width && 
               this.height === other.height;
    }
}

let rect1 = new Rectangle(10, 5);
let rect2 = rect1; // 參照賦值

console.log(rect1.area()); // 50

rect1.scale(2);
console.log(rect1.area()); // 200
console.log(rect2.area()); // 200 - 同一個物件

// 建立獨立副本
let rect3 = rect1.copy();
rect3.scale(0.5);
console.log(rect1.area()); // 200 - 不受影響
console.log(rect3.area()); // 50
```

```go !! go
// Go: 值接收者 vs 指標接收者
package main

import "fmt"

type Rectangle struct {
    Width  float64
    Height float64
}

// 值接收者 - 不修改原結構體
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// 值接收者 - 不能修改原結構體
func (r Rectangle) ScaleByValue(factor float64) Rectangle {
    r.Width *= factor  // 只修改副本
    r.Height *= factor
    return r // 回傳修改後的副本
}

// 指標接收者 - 可以修改原結構體
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor  // 修改原結構體
    r.Height *= factor // Go 自動解參照，等價於 (*r).Width
}

// 指標接收者 - 避免大結構體的拷貝開銷
func (r *Rectangle) String() string {
    return fmt.Sprintf("Rectangle{Width: %.2f, Height: %.2f}", r.Width, r.Height)
}

// 值接收者 - 用於不需要修改的操作
func (r Rectangle) Equals(other Rectangle) bool {
    return r.Width == other.Width && r.Height == other.Height
}

// 指標接收者 - 設定方法
func (r *Rectangle) SetDimensions(width, height float64) {
    r.Width = width
    r.Height = height
}

func main() {
    rect1 := Rectangle{Width: 10, Height: 5}
    rect2 := rect1 // 值拷貝
    
    fmt.Printf("rect1: %s, Area: %.2f\n", rect1.String(), rect1.Area())
    
    // 值接收者方法 - 不修改原結構體
    scaledRect := rect1.ScaleByValue(2)
    fmt.Printf("原始 rect1: %s\n", rect1.String()) // 未改變
    fmt.Printf("縮放後副本: %s\n", scaledRect.String())
    
    // 指標接收者方法 - 修改原結構體
    rect1.Scale(2)
    fmt.Printf("Scale後 rect1: %s\n", rect1.String()) // 已改變
    fmt.Printf("rect2: %s\n", rect2.String()) // 未改變（是副本）
    
    // 透過指標呼叫
    rectPtr := &Rectangle{Width: 3, Height: 4}
    fmt.Printf("指標建立: %s\n", rectPtr.String())
    
    rectPtr.SetDimensions(6, 8)
    fmt.Printf("設定尺寸後: %s\n", rectPtr.String())
    
    // Go 可以自動處理值和指標的轉換
    rect3 := Rectangle{Width: 1, Height: 1}
    rect3.Scale(5) // Go 自動取位址：(&rect3).Scale(5)
    fmt.Printf("自動轉換: %s\n", rect3.String())
    
    rectPtr2 := &Rectangle{Width: 2, Height: 2}
    area := rectPtr2.Area() // Go 自動解參照：(*rectPtr2).Area()
    fmt.Printf("指標呼叫值方法: %.2f\n", area)
}
```
</UniversalEditor>

### 指標與效能最佳化

<UniversalEditor title="效能最佳化對比" compare={true}>
```javascript !! js
// JavaScript: 自動最佳化，開發者控制有限
class LargeObject {
    constructor() {
        // 模擬大物件
        this.data = new Array(1000).fill(0).map((_, i) => ({
            id: i,
            value: Math.random() * 100,
            timestamp: Date.now(),
            metadata: {
                type: 'sample',
                category: 'test',
                description: 'A sample data point'
            }
        }));
    }
    
    // 計算總和 - V8 引擎自動最佳化
    calculateSum() {
        return this.data.reduce((sum, item) => sum + item.value, 0);
    }
    
    // 過濾資料 - 建立新陣列
    filterByValue(threshold) {
        return this.data.filter(item => item.value > threshold);
    }
    
    // 修改資料 - 就地修改
    scaleValues(factor) {
        this.data.forEach(item => {
            item.value *= factor;
        });
    }
}

// 效能測試
console.time('Large Object Creation');
let largeObj = new LargeObject();
console.timeEnd('Large Object Creation');

console.time('Sum Calculation');
let sum = largeObj.calculateSum();
console.timeEnd('Sum Calculation');

console.time('Filtering');
let filtered = largeObj.filterByValue(50);
console.timeEnd('Filtering');

console.log(`Sum: ${sum.toFixed(2)}, Filtered count: ${filtered.length}`);
```

```go !! go
// Go: 顯式記憶體和效能控制
package main

import (
    "fmt"
    "time"
)

type DataPoint struct {
    ID        int
    Value     float64
    Timestamp int64
    Metadata  Metadata
}

type Metadata struct {
    Type        string
    Category    string
    Description string
}

type LargeObject struct {
    Data []DataPoint
}

// 值接收者 - 會拷貝整個結構體（效能較差）
func (lo LargeObject) CalculateSumByValue() float64 {
    var sum float64
    for _, item := range lo.Data { // lo.Data 是副本
        sum += item.Value
    }
    return sum
}

// 指標接收者 - 避免拷貝（效能更好）
func (lo *LargeObject) CalculateSum() float64 {
    var sum float64
    for _, item := range lo.Data { // 直接存取原資料
        sum += item.Value
    }
    return sum
}

// 指標接收者 - 就地修改，避免重新分配
func (lo *LargeObject) ScaleValues(factor float64) {
    for i := range lo.Data {
        lo.Data[i].Value *= factor
    }
}

// 回傳切片而不是拷貝整個陣列
func (lo *LargeObject) FilterByValue(threshold float64) []DataPoint {
    var result []DataPoint
    for _, item := range lo.Data {
        if item.Value > threshold {
            result = append(result, item)
        }
    }
    return result
}

// 就地過濾，重用切片記憶體
func (lo *LargeObject) FilterInPlace(threshold float64) {
    writeIndex := 0
    for _, item := range lo.Data {
        if item.Value > threshold {
            lo.Data[writeIndex] = item
            writeIndex++
        }
    }
    lo.Data = lo.Data[:writeIndex] // 調整切片長度
}

func NewLargeObject() *LargeObject {
    data := make([]DataPoint, 1000)
    for i := range data {
        data[i] = DataPoint{
            ID:        i,
            Value:     float64(i%100) + 0.5,
            Timestamp: time.Now().UnixNano(),
            Metadata: Metadata{
                Type:        "sample",
                Category:    "test",
                Description: "A sample data point",
            },
        }
    }
    return &LargeObject{Data: data}
}

func main() {
    // 建立大物件
    start := time.Now()
    largeObj := NewLargeObject()
    fmt.Printf("物件建立耗時: %v\n", time.Since(start))
    
    // 值接收者效能測試
    start = time.Now()
    sumByValue := largeObj.CalculateSumByValue()
    fmt.Printf("值接收者計算耗時: %v\n", time.Since(start))
    
    // 指標接收者效能測試
    start = time.Now()
    sumByPointer := largeObj.CalculateSum()
    fmt.Printf("指標接收者計算耗時: %v\n", time.Since(start))
    
    fmt.Printf("結果對比 - 值接收者: %.2f, 指標接收者: %.2f\n", 
               sumByValue, sumByPointer)
    
    // 過濾效能測試
    start = time.Now()
    filtered := largeObj.FilterByValue(50)
    fmt.Printf("建立新切片過濾耗時: %v, 結果數量: %d\n", 
               time.Since(start), len(filtered))
    
    // 就地過濾效能測試
    originalLen := len(largeObj.Data)
    start = time.Now()
    largeObj.FilterInPlace(50)
    fmt.Printf("就地過濾耗時: %v, 原長度: %d, 新長度: %d\n", 
               time.Since(start), originalLen, len(largeObj.Data))
    
    // 記憶體使用演示
    fmt.Printf("LargeObject 指標大小: %d 位元組\n", 
               8) // 64位元系統上指標大小
    fmt.Printf("LargeObject 值大小估計: %d 位元組\n", 
               len(largeObj.Data) * 64) // 粗略估計
}
```
</UniversalEditor>

### 常見指標陷阱和最佳實踐

<UniversalEditor title="指標陷阱與最佳實踐" compare={true}>
```javascript !! js
// JavaScript: 常見的參照相關問題
// 1. 意外的共享參照
let originalArray = [1, 2, 3];
let sharedArray = originalArray; // 危險：共享參照

sharedArray.push(4);
console.log(originalArray); // [1, 2, 3, 4] - 意外修改！

// 正確做法：建立副本
let safeCopy = [...originalArray]; // 淺拷貝
let deepCopy = JSON.parse(JSON.stringify(originalArray)); // 深拷貝

// 2. 閉包中的參照陷阱
let functions = [];
for (let i = 0; i < 3; i++) {
    functions.push(() => console.log(i)); // 正確：let 建立區塊作用域
}

functions[0](); // 0
functions[1](); // 1
functions[2](); // 2

// 錯誤範例（使用 var）
let badFunctions = [];
for (var j = 0; j < 3; j++) {
    badFunctions.push(() => console.log(j)); // 陷阱：共享同一個 j
}

badFunctions[0](); // 3
badFunctions[1](); // 3  
badFunctions[2](); // 3

// 3. 回呼函數中的 this 陷阱
class Counter {
    constructor() {
        this.count = 0;
    }
    
    increment() {
        this.count++;
    }
    
    setupTimer() {
        // 錯誤：this 丟失
        // setTimeout(this.increment, 1000);
        
        // 正確：使用箭頭函數或 bind
        setTimeout(() => this.increment(), 1000);
        // 或者：setTimeout(this.increment.bind(this), 1000);
    }
}
```

```go !! go
// Go: 常見指標陷阱和最佳實踐
package main

import "fmt"

type User struct {
    Name string
    Age  int
}

// 陷阱1: 回傳區域變數的指標
func badCreateUser() *User {
    // 危險：回傳堆疊上變數的指標
    user := User{Name: "John", Age: 30}
    return &user // Go 編譯器會自動將其移到堆上，但這不是最佳實踐
}

// 最佳實踐：明確使用堆分配
func goodCreateUser() *User {
    return &User{Name: "John", Age: 30} // 直接在堆上建立
}

// 陷阱2: 空指標解參照
func badProcessUser(user *User) {
    // 危險：沒有檢查 nil
    fmt.Println(user.Name) // 如果 user 是 nil，程式會 panic
}

func goodProcessUser(user *User) {
    // 最佳實踐：總是檢查 nil
    if user == nil {
        fmt.Println("使用者為空")
        return
    }
    fmt.Println(user.Name)
}

// 陷阱3: 在迴圈中建立指標切片
func badCreateUserPointers() []*User {
    var users []*User
    names := []string{"Alice", "Bob", "Charlie"}
    
    for _, name := range names {
        user := User{Name: name, Age: 25}
        users = append(users, &user) // 陷阱：所有指標指向同一個變數
    }
    return users
}

func goodCreateUserPointers() []*User {
    var users []*User
    names := []string{"Alice", "Bob", "Charlie"}
    
    for _, name := range names {
        // 方法1：使用區域變數副本
        userName := name
        user := User{Name: userName, Age: 25}
        users = append(users, &user)
        
        // 方法2：直接在堆上建立
        // users = append(users, &User{Name: name, Age: 25})
    }
    return users
}

// 陷阱4: 指標比較
func demonstratePointerComparison() {
    user1 := &User{Name: "John", Age: 30}
    user2 := &User{Name: "John", Age: 30}
    
    // 陷阱：指標比較比較的是位址，不是值
    fmt.Printf("指標相等: %t\n", user1 == user2) // false
    
    // 正確：比較值
    fmt.Printf("值相等: %t\n", *user1 == *user2) // true
    
    // 或者實作 Equals 方法
    fmt.Printf("自訂比較: %t\n", user1.Equals(*user2))
}

func (u User) Equals(other User) bool {
    return u.Name == other.Name && u.Age == other.Age
}

// 最佳實踐：方法接收者選擇
type Counter struct {
    count int
    data  []int // 大資料
}

// 不需要修改：使用值接收者（小結構體）
func (c Counter) GetCount() int {
    return c.count
}

// 需要修改：使用指標接收者
func (c *Counter) Increment() {
    c.count++
}

// 大結構體：使用指標接收者避免拷貝
func (c *Counter) ProcessData() int {
    var sum int
    for _, v := range c.data {
        sum += v
    }
    return sum
}

// 最佳實踐：錯誤處理與指標
func findUser(id int) (*User, error) {
    if id <= 0 {
        return nil, fmt.Errorf("無效的使用者ID: %d", id)
    }
    
    // 模擬資料庫查詢
    if id == 1 {
        return &User{Name: "Alice", Age: 25}, nil
    }
    
    return nil, fmt.Errorf("使用者未找到: %d", id)
}

func main() {
    fmt.Println("=== 指標陷阱演示 ===")
    
    // 演示陷阱1
    user := badCreateUser()
    fmt.Printf("Bad create user: %+v\n", user)
    
    user = goodCreateUser()
    fmt.Printf("Good create user: %+v\n", user)
    
    // 演示陷阱2
    fmt.Println("\n=== 空指標檢查 ===")
    goodProcessUser(user)
    goodProcessUser(nil)
    
    // 演示陷阱3
    fmt.Println("\n=== 迴圈中的指標 ===")
    badUsers := badCreateUserPointers()
    fmt.Println("錯誤的實作:")
    for i, u := range badUsers {
        fmt.Printf("  使用者%d: %+v\n", i, *u)
    }
    
    goodUsers := goodCreateUserPointers()
    fmt.Println("正確的實作:")
    for i, u := range goodUsers {
        fmt.Printf("  使用者%d: %+v\n", i, *u)
    }
    
    // 演示陷阱4
    fmt.Println("\n=== 指標比較 ===")
    demonstratePointerComparison()
    
    // 最佳實踐：錯誤處理
    fmt.Println("\n=== 錯誤處理 ===")
    if user, err := findUser(1); err != nil {
        fmt.Printf("錯誤: %v\n", err)
    } else {
        fmt.Printf("找到使用者: %+v\n", user)
    }
    
    if user, err := findUser(-1); err != nil {
        fmt.Printf("錯誤: %v\n", err)
    } else {
        fmt.Printf("找到使用者: %+v\n", user)
    }
}
```
</UniversalEditor>

### 指標最佳實踐總結

| 使用場景 | JavaScript | Go 指標最佳實踐 |
|----------|------------|----------------|
| **函數參數** | 物件自動參照傳遞 | 大結構體使用指標，小值型別傳值 |
| **方法接收者** | 自動綁定 this | 需要修改或大結構體使用指標接收者 |
| **效能最佳化** | 引擎自動最佳化 | 避免不必要的拷貝，使用指標 |
| **記憶體管理** | 自動垃圾回收 | 檢查 nil，避免記憶體洩漏 |
| **錯誤處理** | try-catch 或 Promise | 回傳指標和錯誤，檢查 nil |
| **比較操作** | == 比較值或參照 | 指標比較位址，需要解參照比較值 |

**關鍵原則：**
1. **小物件傳值，大物件傳指標**
2. **需要修改時使用指標**
3. **總是檢查 nil 指標**
4. **在迴圈中小心指標的生命週期**
5. **使用指標提高效能，但要注意記憶體安全**

## 型別嵌入和組合

Go 使用組合而不是繼承，透過結構體嵌入實現。

<UniversalEditor title="型別嵌入對比" compare={true}>
```javascript !! js
// JavaScript: 類繼承和組合
class Animal {
    constructor(name) {
        this.name = name;
    }
    
    speak() {
        return `${this.name} makes a sound`;
    }
}

class Dog extends Animal {
    constructor(name) {
        super(name);
    }
    
    speak() {
        return `${this.name} barks`;
    }
    
    fetch() {
        return `${this.name} fetches the ball`;
    }
}

// 組合
class Walker {
    walk() {
        return "Walking...";
    }
}

class Swimmer {
    swim() {
        return "Swimming...";
    }
}

class Duck {
    constructor(name) {
        this.name = name;
        this.walker = new Walker();
        this.swimmer = new Swimmer();
    }
    
    walk() {
        return this.walker.walk();
    }
    
    swim() {
        return this.swimmer.swim();
    }
}

let dog = new Dog("Rex");
let duck = new Duck("Donald");
console.log(dog.speak()); // "Rex barks"
console.log(duck.walk()); // "Walking..."
```

```go !! go
// Go: 結構體嵌入 (組合)
package main

import "fmt"

// 基礎結構體
type Animal struct {
    Name string
}

func (a Animal) Speak() string {
    return fmt.Sprintf("%s makes a sound", a.Name)
}

// 在 Dog 中嵌入 Animal
type Dog struct {
    Animal // 嵌入結構體
}

func (d Dog) Speak() string {
    return fmt.Sprintf("%s barks", d.Name)
}

func (d Dog) Fetch() string {
    return fmt.Sprintf("%s fetches the ball", d.Name)
}

// 多嵌入型別的組合
type Walker struct{}

func (w Walker) Walk() string {
    return "Walking..."
}

type Swimmer struct{}

func (s Swimmer) Swim() string {
    return "Swimming..."
}

type Duck struct {
    Animal
    Walker
    Swimmer
}

func main() {
    dog := Dog{Animal{Name: "Rex"}}
    duck := Duck{
        Animal: Animal{Name: "Donald"},
        Walker: Walker{},
        Swimmer: Swimmer{},
    }
    
    fmt.Println(dog.Speak()) // "Rex barks"
    fmt.Println(dog.Fetch()) // "Rex fetches the ball"
    fmt.Println(duck.Walk()) // "Walking..."
    fmt.Println(duck.Swim()) // "Swimming..."
    
    // 透過嵌入實現介面滿足
    type Speaker interface {
        Speak() string
    }
    
    var speakers []Speaker = []Speaker{dog, duck}
    for _, speaker := range speakers {
        fmt.Println(speaker.Speak())
    }
}
```
</UniversalEditor>

---

### 練習題：
1. 解釋 Go 的靜態型別與 JavaScript 動態型別的區別。每種方法的優缺點是什麼？
2. Go 介面與 JavaScript 的鴨子型別有何不同？提供每種方法有益的場景範例。
3. 描述 Go 的結構體嵌入以及它與 JavaScript 類繼承的比較。何時使用每種方法？
4. 建立一個 Go 程式，演示介面組合、型別斷言和型別開關的實際範例。
5. **指標練習**：比較 JavaScript 的參照傳遞和 Go 的指標傳遞。什麼時候應該使用值接收者，什麼時候使用指標接收者？
6. **記憶體管理練習**：編寫一個 Go 程式，演示指標如何影響效能。比較值傳遞和指標傳遞在大結構體上的效能差異。
7. **指標陷阱練習**：識別並修復以下常見的指標陷阱：空指標解參照、迴圈中的指標問題、指標比較錯誤。

### 專案想法：
* **形狀計算器**：在 Go 中建構一個簡單的形狀計算器，使用介面處理不同的幾何形狀（圓形、矩形、三角形）。實作面積和周長計算，並使用型別開關處理不同的形狀型別。與使用類和繼承的 JavaScript 實作進行比較。
* **記憶體管理系統**：建立一個簡單的記憶體池管理系統，演示指標的使用。包括物件建立、回收和重用，比較指標和值型別在記憶體效率上的差異。
* **鏈結串列資料結構**：實作一個雙向鏈結串列，充分利用 Go 的指標特性。包含插入、刪除、遍歷等操作，並與 JavaScript 的物件參照實作進行效能對比。

### 下一步：
* 學習 Go 的並發特性：goroutines 和 channels
* 探索 Go 的錯誤處理模式和最佳實踐
* 理解 Go 的套件管理和模組系統
